require(
	["jquery", "webgl", "assetSyst", "assetShader", "assetShaderProgram", "jsonModel", "cam", "transform", "timer", "glMatrix"], 
	function($, webgl, assetSyst, assetShader, asp, jsonModel, cam, transform, timer) {
	
		var model;
		var program;
		var entities;
		var ccam;
		var ttimer;
		var frame;
		var tempMat3 = mat3.create();
		
		function areAllLoaded() {
			return (model && model.isLoaded() && program && program.isLoaded());
		}
		
		function pointAttrib(attrib, buf) {
			var gl = webgl.gl;
			gl.bindBuffer(gl.ARRAY_BUFFER, buf);
			gl.vertexAttribPointer(program.getAttribute(attrib), buf.itemSize, gl.FLOAT, false, 0, 0);
		}
		
		function drawEnt(ent) {
			var gl = webgl.gl;
				
			//set transform
			gl.uniformMatrix4fv(program.getUniform("uMMat"), false, ent.transform.getMatrix());
			
			//normal transform
			mat4.toInverseMat3(ent.transform.getMatrix(), tempMat3);
			mat3.transpose(tempMat3);
			gl.uniformMatrix3fv(program.getUniform("uNMat"), false, tempMat3);
			
			//draw
			gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ent.model.buf.indices);
			gl.drawElements(gl.TRIANGLES, ent.model.buf.indices.numItems, gl.UNSIGNED_SHORT, 0);
		}
	
		function draw() {		
			var gl 	= webgl.gl;
			
			//Clear screen
			gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
			
			//Set program
			gl.useProgram(program.program);
			
			//set camera matrices
			gl.uniformMatrix4fv(program.getUniform("uVMat"), false, ccam.getView());
			gl.uniformMatrix4fv(program.getUniform("uPMat"), false, ccam.getPerspective());
		
			//set attrib
			var model = entities[0].model;
			
			if(model.isLoaded()) {
				//set attrib
				pointAttrib("aPos", entities[0].model.buf.pos);
				pointAttrib("aNormal", entities[0].model.buf.normal);
				
				//draw each entity
				for(var i = 0; i < entities.length; ++i) {
					drawEnt(entities[i]);
				}
			}
		}
		
		function createEnt() {
			var ent = {
				model 		: assetSyst.get("teapot.json", function(){}),
				transform	: transform.create()
			};
			
			ent.transform.pos[0] = Math.random() * 100;
			ent.transform.pos[1] = Math.random() * 100 - 50;
			ent.transform.pos[2] = Math.random() * 100;
			ent.transform.rot[0] = Math.random() * 100;
			ent.transform.rot[1] = Math.random() * 100;
			ent.transform.rot[2] = Math.random() * 100;
			ent.transform.update();
			
			return ent;
		}
		
		function randomMove(ent) {
			//ent.transform.pos[0] += (20 * Math.random() - 10) * ttimer.lastDelta();
			//ent.transform.pos[1] += (20 * Math.random() - 10) * ttimer.lastDelta();
			//ent.transform.pos[2] += (20 * Math.random() - 10) * ttimer.lastDelta();
			ent.transform.rot[0] += (1) * ttimer.lastDelta();
			//ent.transform.rot[1] += (10 * Math.random() - 5) * ttimer.lastDelta();
			//ent.transform.rot[2] += (10 * Math.random() - 5) * ttimer.lastDelta();
			ent.transform.update();
		}
		
		function animate() {
			for(var i = 0; i < entities.length; ++i) {
				randomMove(entities[i]);
			}
		}
		
		function tick() {
			//
			++frame;
		
			//update
			ttimer.update();
			animate();
			
			//render
			draw();
			
			requestAnimFrame(tick);
		}
		
		function printFPS() {
			$("#FPS").text("FPS : " + frame);
			frame = 0;
		}
	
		$(function() {
			//Init webgl
			if(!webgl.init($('canvas')[0], true)) return;
		
			//asset mapping
			assetSyst.mapExt("shader", "shader/", assetShader);
			assetSyst.mapExt("json", "model/", jsonModel);
			assetSyst.mapExt("sp", "", asp);
			
			//load shader
			program = assetSyst.get("vtest|ftest|shader.sp", function(){});
			
			//load entities
			entities = [];
			for(var i = 0; i < 10; ++i) {
				entities[i] = createEnt();
			}
			
			//init camera
			ccam		= cam.create();
			ccam.pos 	= vec3.create([200, 0, 200]);
			ccam.aspect	= webgl.viewportWidth/webgl.viewportHeight;
			ccam.update();
			
			//init timer
			ttimer = timer.create();
			ttimer.reset();
			
			//init graphic settings
			var gl = webgl.gl;
			gl.viewport(0,0,webgl.viewportWidth,webgl.viewportHeight);
			gl.clearColor(0,0,0,1);
			gl.enable(gl.DEPTH_TEST);
			
			//start
			tick();
			
			//fps info
			setInterval(printFPS, 1000);
		});
});
